package cn.geminis.workflow.service.controller;

import cn.geminis.auth.context.LoginUser;
import cn.geminis.data.jpa.PageQuery;
import cn.geminis.workflow.service.controller.vo.Approve;
import cn.geminis.workflow.service.controller.vo.Task;
import cn.geminis.workflow.service.entity.ApproveResult;
import cn.geminis.workflow.service.service.approve.ApproveService;
import cn.geminis.workflow.service.service.engine.expression.ExpressionValueContext;
import cn.geminis.workflow.service.utils.QueryUtils;
import cn.geminis.workflow.service.utils.SpringUtils;
import cn.geminis.workflow.service.utils.WorkflowUtils;
import lombok.AllArgsConstructor;
import org.flowable.engine.CandidateManager;
import org.flowable.engine.ProcessEngine;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.springframework.data.domain.Page;
import org.springframework.util.ObjectUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.WebApplicationContext;

import java.util.ArrayList;
import java.util.List;

/**
 * @author puddi
 */
@RestController
@RequestMapping("/task")
@AllArgsConstructor
public class TaskController {

    private final WebApplicationContext context;
    private final ProcessEngine engine;
    private final CandidateManager candidateManager;

    @GetMapping("/my")
    public Page<Task> myTasks(final PageQuery pageParameter) {
        var username = LoginUser.fullName();
        var groups = this.candidateManager.getGroupsForCandidateUser(username);

        var query = engine
                .getTaskService()
                .createTaskQuery()
                .taskCandidateGroupIn(groups)
                .taskCandidateOrAssigned(username)
                .orderByTaskCreateTime().desc();

        return QueryUtils.toPage(query, pageParameter, Task.class);
    }

    @PutMapping(value = {"/{businessKey}/assignee/{instanceId}", "/{businessKey}/assignee/", "/{businessKey}/assignee"})
    public void assignee(
            @PathVariable String businessKey,
            @PathVariable(required = false) String instanceId) {
        if (ObjectUtils.isEmpty(instanceId)) {
            var task = this.engine.getTaskService().createTaskQuery()
                    .processInstanceBusinessKey(businessKey)
                    .singleResult();
            instanceId = task.getId();
        }
        this.engine.getTaskService()
                .setAssignee(instanceId, LoginUser.fullName());
    }

    @PutMapping("/complete")
    public void complete(@RequestBody Approve approve) {
        var valueContext = SpringUtils.getBean(ExpressionValueContext.class);
        valueContext.clear();

        if (ObjectUtils.isEmpty(approve.getTaskInstanceId())) {
            var task = engine.getTaskService()
                    .createTaskQuery()
                    .processInstanceBusinessKey(approve.getBusinessKey())
                    .singleResult();

            if (!task.getTaskDefinitionKey().equals(approve.getTaskKey())) {
                throw new RuntimeException("任务" + approve.getTaskKey() + "未执行");
            }

            approve.setProcessInstanceId(task.getProcessInstanceId());
            approve.setTaskInstanceId(task.getId());
        }
        var approveService = this.context.getBean(approve.getResult(), ApproveService.class);
        approveService.handle(cn.geminis.workflow.service.utils.ObjectUtils.convert(approve, ApproveResult.class));
    }

    @GetMapping(value = {"/{businessKey}/canReturn/{instanceId}", "/{businessKey}/canReturn"})
    public List<Task> canReturn(@PathVariable String businessKey, @PathVariable(required = false) String instanceId) {
        var query = engine.getTaskService().createTaskQuery();
        if (ObjectUtils.isEmpty(instanceId)) {
            query = query.processInstanceBusinessKey(businessKey);
        } else {
            query = query.taskId(instanceId);
        }
        var taskInstance = query.singleResult();

        var taskInstances = engine.getHistoryService()
                .createHistoricTaskInstanceQuery()
                .processInstanceId(taskInstance.getProcessInstanceId())
                .taskWithoutDeleteReason()
                .list();

        var bpmnModel = engine.getRepositoryService().getBpmnModel(taskInstance.getProcessDefinitionId());
        var task = (org.flowable.bpmn.model.Task) bpmnModel.getFlowElement(taskInstance.getTaskDefinitionKey());
        var result = new ArrayList<HistoricTaskInstance>();
        WorkflowUtils.taskInstancesBackwardBetween2Tasks(task, null, taskInstances, result);

        return cn.geminis.workflow.service.utils.ObjectUtils.convert(result, Task.class);
    }
}
